////////////////////////////////////////////////////////////////////////////////
//
//  The MIT License
//
//  Copyright (c) 2008 - 2009. Dimarik
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package tx.framework.service {

	import flash.events.AsyncErrorEvent;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.NetStatusEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.NetConnection;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.utils.ByteArray;
	import flash.utils.clearTimeout;
	import flash.utils.setTimeout;

	import tx.astrum.view.dialog.ErrorDialog;
	import tx.framework.ioc.control.IocInterpreter;
	import tx.framework.logging.Log;
	import tx.framework.logging.LogEventLevel;
	import tx.framework.utils.ObjectUtil;

	public final class AMFService extends NetConnection {

		public static var pollTimeout:	uint = 5000;

		private static var _instance:	AMFService;

		public static function getInstance():AMFService {
			if (_instance == null) _instance = new AMFService();
			return _instance;
		}

		//---------------------------------------------------------------------
		//
		//   Constructor
		//
		//---------------------------------------------------------------------

		public function AMFService() {
			super();
			super.addEventListener(NetStatusEvent.NET_STATUS,			this.handler_status);
			super.addEventListener(IOErrorEvent.IO_ERROR,				this.handler_IOError);
			super.addEventListener(AsyncErrorEvent.ASYNC_ERROR,			this.handler_asyncError);
			super.addEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_securityError);
		}

		//---------------------------------------------------------------------
		//
		//   Properties
		//
		//---------------------------------------------------------------------

		//-----------------------------
		//  currentMethod
		//-----------------------------

		private var _currentMethod:	String;

		public function get currentMethod():String {
			return this._currentMethod;
		}

		//---------------------------------------------------------------------
		//
		//   Private variables
		//
		//---------------------------------------------------------------------

		private var lastMessageId:		String = null;

		private var intervalId:			int;

		private var polling:			Boolean;

		private var isPollingNow:		Boolean;

		private var _currentArgs:		Array;

		//---------------------------------------------------------------------
		//
		//   Overriden methods: NetConnection
		//
		//---------------------------------------------------------------------

		public override function call(method:String, responder:flash.net.Responder, ... args):void {
			this._currentMethod = method;
			this._currentArgs = args;

			var arr:	Array = new Array();
			var cmd:	String = method;

			for (var i:uint = 0; i < args.length; i++) {
				cmd += ' {' + i + '}';
				arr.push(args[i]);
			}

			arr.unshift(LogEventLevel.INFO, cmd);

			if (method != 'DWService.cmdPool') Log.getLogger("DW-OUT").log.apply(null, arr);

			args.unshift(method, responder);

			super.call.apply(this, args);
		}


		//---------------------------------------------------------------------
		//
		//   Public methods
		//
		//---------------------------------------------------------------------

		public function stopPoll():void {
			clearTimeout(this.intervalId);
			this.polling = false;
		}

		public function startPoll():void {
			this.polling = true;
			this.doPoll();
		}

		public function poll():void {
			this.call(
				"DWService.cmdPool",
				new flash.net.Responder(this.pollSucces, this.pollFault),
				this.lastMessageId ? this.lastMessageId : 0
			);
			this.isPollingNow = true;
		}

		public function pollSucces(data:Object):void {
			this.isPollingNow = false;

			if (data) {
				Log.getLogger("IN-POLL-RAW").info(ObjectUtil.toString(data));
				
				if ( data.hasOwnProperty('msgCode') ) {
					

					switch ( parseInt(data.msgCode) ) {
						case 13: // You are not logged in
							ErrorDialog.open().content = data.msgText;
							this.stopPoll();
							break;

						default:
							ErrorDialog.open().content = data.msgText;
					}
					return;	
				}



				if (data is Array) {
					for (var i:int = 0; i < data.length; i++) {
						this.lastMessageId = data[i]["id"];
						var message:String = data[i]["message"];
						if (message.charAt(0) == "@") {
							Log.getLogger("IN-POLL").info(message.substr(1));
							IocInterpreter.getInstance().execute(message.substr(1));
						} else {
							Log.getLogger("MESSAGE").info(message);
						}
					}
				}

			}

			this.doPoll();
		}

		public function pollFault(data:Object):void {
			this.isPollingNow = false;
			Log.getLogger("POLL").error(data.toString());
			this.doPoll();
		}

		//---------------------------------------------------------------------
		//
		//   Private methods
		//
		//---------------------------------------------------------------------

		private function doPoll():void {
			clearTimeout(this.intervalId);
			if (this.polling) {
				this.intervalId = setTimeout(this.poll, pollTimeout);
			}
		}

		private function encodeToAMF(command:String, ... args):ByteArray {
			return null;
		}

		private function getServerRawReply():void {
			var request:URLRequest = new URLRequest(this.uri);
			request.method = URLRequestMethod.POST;
			request.contentType = URLLoaderDataFormat.BINARY;
			request.data = this.encodeToAMF(this._currentMethod, this._currentArgs);

			var loader:URLLoader = new URLLoader(request);
			loader.addEventListener(Event.COMPLETE,						this.handler_loadReplyComplete);
			loader.addEventListener(IOErrorEvent.IO_ERROR,				this.handler_loadReplyFailed);
			loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_loadReplyFailed);
		}

		//---------------------------------------------------------------------
		//
		//   Events handlers
		//
		//---------------------------------------------------------------------

		private function handler_loadReplyComplete(event:Event):void {
			var loader:URLLoader = event.target as URLLoader;
			loader.removeEventListener(Event.COMPLETE,						this.handler_loadReplyComplete);
			loader.removeEventListener(IOErrorEvent.IO_ERROR,				this.handler_loadReplyFailed);
			loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_loadReplyFailed);
			Log.getLogger("STATUS").log.apply(null, [LogEventLevel.INFO, loader.data]);
		}

		private function handler_loadReplyFailed(event:Event):void {
			var loader:URLLoader = event.target as URLLoader;
			loader.removeEventListener(Event.COMPLETE,						this.handler_loadReplyComplete);
			loader.removeEventListener(IOErrorEvent.IO_ERROR,				this.handler_loadReplyFailed);
			loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_loadReplyFailed);
			Log.getLogger("STATUS").log.apply(null, [LogEventLevel.INFO, 'Row server reply failed']);
		}

		private function handler_status(event:NetStatusEvent):void {
			// do nothing
			trace('handler_status: ' + event);
			Log.getLogger("STATUS").log.apply(null, [LogEventLevel.INFO, event.info.code]);
			this.getServerRawReply();
		}

		private function handler_IOError(event:IOErrorEvent):void {
			// do nothing
			Log.getLogger("ERROR").log.apply(null, [LogEventLevel.ERROR, event.text]);
		}

		private function handler_asyncError(event:AsyncErrorEvent):void {
			Log.getLogger("ERROR").log.apply(null, [LogEventLevel.ERROR, event.text]);
		}

		private function handler_securityError(event:SecurityErrorEvent):void {
			Log.getLogger("ERROR").log.apply(null, [LogEventLevel.ERROR, event.text]);
		}


	}
}